/*
 * @Author: wws1993
 * @Date: 2022-04-01 16:24:32
 * @LastEditTime: 2023-03-29 11:03:27
 * @LastEditors: wws1993 15696141050@163.com
 * @Description: 优化生成的api文件，使其可以直接投入使用（js版本）
 * @FilePath: \swagger2Api1.0\v2.0\main.js
 * IE6粉丝头子
 * @history: v1.0
 */

const fs = require('fs')
const request = require('request');
const config = require('./config')

/** @type {import('./type.d').ConfigKey} */
let currentMode;

currentMode = 'scm_applet_driver'
// currentMode = 'scm_applet_admin'
currentMode = 'scm_pc'

console.log('当前产出配置：', currentMode);

const {
  swaggerurl,
  commonReqKeys,
  root,
  apiFileName,
  apiTemplateForHeader,
  apiTemplateForItem,
  entryImportType
} = config[currentMode]

/** 处理中文名实例 */
const cnNames = {}

/** 获取api文档 */
const createApi = () => {
  request(swaggerurl, { json: true }, (err, res, body) => {
    try {
      handleExportDtos(body.definitions)
      handleExportApis(body.paths)
    } catch (error) {
      console.log('=======================   代码出错啦   =======================')
      console.log(error);
    }
  });
}

/** 导出Api */
const handleExportApis = paths => {
  let apiDoc = ''

  for (const url in paths) {
    if (url === '/vehicleCheckProject/vehicleCheckProjectSubmit') {
      console.log(url);
    }

    for (const method in paths[url]) {
      const instance = paths[url][method]
      // req-params
      const parameters = instance.parameters                   // 入参
      const paramsMap = {}                                     // 入参Map
      let   reqDto = ''                                        // 将导出的入参
      if (parameters?.length) {
        for (const paramItem of parameters) {
          if (paramItem?.schema) {
            if (paramItem.schema.$ref) {
              let sourceName = paramItem.schema.$ref.split('/').pop()
              sourceName = /[\u4e00-\u9fa5]/.test(sourceName) ? cnNames[sourceName] : sourceName
              const fmtSourceName = removeSymbol(sourceName)

              if (!apiDoc.includes(`\nimport ${fmtSourceName} `)) {
                if (entryImportType === 'ts') apiDoc = `\nimport ${fmtSourceName} from './dto/${fmtSourceName}';` + apiDoc
              }
              paramsMap[paramItem.name] = fmtSourceName
            } else {
              paramsMap[paramItem.name] = fmtDataType(paramItem.schema.type)
            }
          } else {
            paramsMap.params = paramsMap.params || {}
            paramsMap.params[paramItem.name] = fmtDataType(paramItem.type)
          }
        }

        reqDto = JSON.stringify(Object.values(paramsMap)[0]).replace(/"/g, '')
      }

      // res-response
      const response = instance.responses['200']
      let resDto = ''
      if (response) {
        if (response?.schema?.$ref) {
          let sourceName = response.schema.$ref.split('/').pop()
          let _sourceName = /[\u4e00-\u9fa5]/.test(sourceName) ? cnNames[sourceName] : sourceName
          const fmtSourceName = removeSymbol(_sourceName)

          if (!apiDoc.includes(`\nimport ${fmtSourceName} `)) {
            if (entryImportType === 'ts') apiDoc = `\nimport ${fmtSourceName} from './dto/${fmtSourceName}';` + apiDoc
          }

          resDto = fmtSourceName
        } else resDto = 'void'
      }

      apiDoc += apiTemplateForItem
        .replace('{{url}}', url)
        .replace('{{apiDesc}}', instance.summary)
        .replace('{{apiName}}', `${instance.operationId}Api`)
        .replace('{{reqDto}}', (fmtDataType(reqDto) !== reqDto || !reqDto) ? 'any' : getEntryImportCode(reqDto))
        .replace('{{resDto}}', resDto.includes('void') ? 'any' : getEntryImportCode(resDto))
    }
  }

  apiDoc = `${apiTemplateForHeader}${apiDoc}\n`

  save2file(`./${root}/${apiFileName}`, apiDoc)
}

/** 处理实例 */
const handleExportDtos = definitions => {
  const entrys = Object.keys(definitions).sort((a, b) => a.length - b.length)
  // console.log(JSON.stringify(entrys));
  // throw Error('end')
  entrys.forEach(dtoKey => {
    let instanceName = dtoKey
    // 有一部分中文名实例，将其转为英文名实例
    if (/[\u4e00-\u9fa5]/.test(dtoKey)) {
      instanceName = `cnDto${Object.keys(cnNames).length + 1}`
      cnNames[dtoKey] = instanceName
    }

    const item = definitions[dtoKey]
    let docs = ``

    if (item.properties) {
      docs += `/** ${item.description || item.title} */\ntype ${removeSymbol(instanceName)} = {`

      for (const key in item.properties) {                                                          // 循环实例配置
        const row = item.properties[key]
        if (row.$ref) {                                                                             // 若有引用
          const sourceName = row.$ref.split('/').pop()                                              // 获取引用的文件名
          const _sourceName = /[\u4e00-\u9fa5]/.test(sourceName) ? cnNames[sourceName] : sourceName
          const fmtSourceName = removeSymbol(_sourceName)                                           // 格式化文件名
          fmtSourceName !== instanceName ? docs = `import ${fmtSourceName} from './${fmtSourceName}'\n` + docs : null // 引用头部添加
          docs += `\n  /** ${row.description || definitions[sourceName].description || ''} */`      // 添加字段注释
          docs += `\n  ${key}${isRequired(item, row, key)}: ${fmtSourceName};`                      // 添加字段及类型
        } else if (row.type == 'array') {                                                           // 字段类型为array的特殊处理
          if (row.items.$ref) {
            const sourceName = row.items.$ref.split('/').pop()                                      // 获取引用的文件名
            const _sourceName = /[\u4e00-\u9fa5]/.test(sourceName) ? cnNames[sourceName] : sourceName
            const fmtSourceName = removeSymbol(_sourceName)                                         // 格式化文件名
            fmtSourceName !== instanceName ? docs = `import ${fmtSourceName} from './${fmtSourceName}'\n` + docs : null // 引用头部添加
            docs += `\n  /** ${row.description || definitions[sourceName].description || ''} */`    // 添加字段注释
            docs += `\n  ${key}${isRequired(item, row, key)}: ${fmtSourceName}[];`                  // 添加字段及类型
          } else {
            docs += `\n  /** ${row.description || ''} */`                                           // 添加字段注释
            docs += `\n  ${key}${isRequired(item, row, key)}: ${fmtDataType(row.items.type)}[];`    // 添加字段及类型
          }
        } else {
          docs += `\n  /** ${row.description || ''} */`                                             // 添加字段注释
          docs += `\n  ${key}${isRequired(item, row, key)}: ${fmtDataType(row.type)};`              // 添加字段及类型
        }
      }

      docs += `\n}\n\nexport default ${removeSymbol(instanceName)}\n`
    }

    save2file(`./${root}/dto/${removeSymbol(instanceName)}.ts`, docs)
  })

  console.log(cnNames);
}

/** 文件导出 */
const save2file = (name, data) => {
  if (!data) return

  fs.writeFile(name, data, err => {
    if (err) console.log(name, 'faild');
  })
}

/** 移除特殊符号 */
const removeSymbol = (name = '') => {
  return name.replace(/[»|«|,]/g, '')
}

/** 请求地址转关键字 */
const url2keyword = (url = '') => {
  return url.split(/[-|\/]/g).filter(k => k && !k.includes('{')).map(k => toUpperFirst(k)).join('')
}

/** 获取实例引入代码
 * @param {string} entryName 实例名
 * @example xxEntry => xxEntry // ts引入方式
 * @example xxEntry => import('./dto/xxEntry').default // js引入方式
 */
const getEntryImportCode = entryName => {
  return entryImportType === 'ts'
    ? entryName
    : `import('./dto/${entryName}').default`
}

/** 首字母大写 */
const toUpperFirst = (str = '') => `${str[0].toUpperCase()}${str.slice(1)}`

/** 格式化数据类型 */
const fmtDataType = type => {
  switch (type) {
    case 'integer':
      return 'number';
    case 'string':
      return 'string';
    case 'number':
      return 'number';
    case 'boolean':
      return 'boolean';
    case 'object':
      return 'object';
    case 'file':
      return 'File';
    default:
      return type;
  }
}

const isRequired = (item, row, key) => {
  let res = true;

  if (item.title.includes('Req')) {
    res = item.required ? item.required.includes(key) : false
  }

  if (commonReqKeys.includes(key)) res = false

  return res ? '' : '?'
}

fs.rm(`./${root}`, {recursive: true}, err => {
  fs.mkdirSync(`./${root}`)
  fs.mkdirSync(`./${root}/dto`)
  createApi()
  // setTimeout(() => {
  //   console.log(cnNames);
  // }, 500);
})
